package bank;

import java.util.List;
import java.util.ArrayList;

/**
This bank contains a collection of bank accounts.
*/
public class Bank
{   
   
	// Attributes
	private List<BankAccount> accounts;
	
   /**
   Constructs a bank with no bank accounts.
   */
   public Bank()
   {
      accounts = new ArrayList<BankAccount>();
   }
   
   /**
   Adds an account to this bank.
   @param a the account to add
   */
   public void addAccount(BankAccount a)
   {
      accounts.add(a);
   }
   
   /**
   Adds a checking account to this bank.
   @param accountNumber account number of the new account
   @param initialBalance initial funds of the new account
   */
   public void addCheckingAccount(int accountNumber, double initialBalance)
   {
      accounts.add(new CheckingAccount(accountNumber, initialBalance));
   }
   
   /**
   Adds a savings account to this bank.
   @param accountNumber account number of the new account
   @param initialBalance initial funds of the new account
   @param interestRate interest rate of the new account
   */
   public void addSavingsAccount(int accountNumber, double initialBalance, double interestRate)
   {
      accounts.add(new SavingsAccount(accountNumber, initialBalance, interestRate));
   }
   
   /**
   Gets the sum of the balances of all accounts in this bank.
   @return the sum of the balances
   */
   public double getTotalBalance()
   {
      double total = 0;
      for (BankAccount a : accounts)
      {
         total = total + a.getBalance();
      }
      return total;
   }

   /**
   Counts the number of bank accounts whose balance is at
   least a given value.
   @param atLeast the balance required to count an account
   @return the number of accounts having least the given balance
   */
   public int count(double atLeast)
   {
      int matches = 0;
      for (BankAccount a : accounts)
      {
         if (a.getBalance() >= atLeast) matches++; // Found a match
      }
      return matches;
   }

   /**
   Finds a bank account with a given number.
   @param accountNumber the number to find
   @return the account with the given number, or null if there
   is no such account
   */
   public BankAccount find(int accountNumber)
   {
      for (BankAccount a : accounts)
      {
         if (a.getAccountNumber() == accountNumber) // Found a match
            return a;
      } 
      return null; // No match in the entire array list
   }

   /**
   Gets the bank account with the largest balance.
   @return the account with the largest balance, or null if the
   bank has no accounts
   */
   public BankAccount getMaximum()
   {
      if (accounts.size() == 0) return null;
      BankAccount largestYet = accounts.get(0);
      for (int i = 1; i < accounts.size(); i++) 
      {
         BankAccount a = accounts.get(i);
         if (a.getBalance() > largestYet.getBalance())
            largestYet = a;
      }
      return largestYet;
   }
   
   /**
   makes a deposit
   @param acctNum the account number
   @param amount the amount of the transaction
   */
   public void deposit(int acctNum, double amount)
   {
	   this.find(acctNum).deposit(amount);
   }
   
   /**
   makes a withdrawal
   @param acctNum the account number
   @param amount the amount you wish to withdraw
   */
   public void withdraw(int acctNum, double amount)
   {
	   this.find(acctNum).withdraw(amount);
   }
   
   /**
   Gets you the balance of the account
   @param acctNum the account number
   @return the balance
   */
   public double getBalance(int acctNum)
   {
	   return this.find(acctNum).getBalance();
   }
   
   /**
   Suspends the requested account (unless it is closed)
   @param acctNum the account number
   */
   public void suspendAccount(int acctNum)
   {
	   this.find(acctNum).suspend();
   }
   
   /**
   Opens the requested account (unless it is closed)
   @param acctNum the account number
   */
   public void openAccount(int acctNum)
   {
	   this.find(acctNum).open();
   }
   
   /**
   Closes the requested account after withdrawing all
   available funds
   @param acctNum the account number
   */
   public void closeAccount(int acctNum)
   {
	   this.find(acctNum).withdraw(this.find(acctNum).getBalance());
	   this.find(acctNum).close();
   }
   
   /**
   Gets the status of the account
   @param acctNum the account number
   @return the status of the account
   */
   public String getAccountStatus(int acctNum)
   {
	   return this.find(acctNum).getStatus();
   }
   
   /**
   Gets all transactions for the requested account
   @param acctNum the account number
   @return a string of the transactions of this account
   */
   public String accountTransactionsString(int acctNum)
   {
	   return this.find(acctNum).transactionsString();
   }
   
   /**
   Summarizes all of the accounts
   @return a String - the summary of all the accounts
   */
   public String summarizeAllAccounts()
   {
	   String outputString = "Bank Account Summary\n\nAccount\t\tType\t\tBalance\t\t#Transactions\t\tStatus\n";
	   for (BankAccount account : accounts)
	   {
		   outputString = outputString + account.getAccountNumber() + "\t\t";
		   if (account instanceof CheckingAccount)
		   {
			   outputString = outputString + "checking\t";
		   }
		   else if (account instanceof SavingsAccount)
		   {
			   outputString = outputString + "savings\t\t";
		   }
		   outputString = outputString + account.getBalance() + "\t\t     ";
		   outputString = outputString + account.numTransactions() + "\t\t\t";
		   outputString = outputString + account.getStatus() + "\n";
	   }
	   outputString = outputString + "End of Account Summary";
	   return outputString;
   }
   
   /**
   Finds the account and calls addInterest if it is a savings account
   @param accountNumber the number of the account to find
   */
   public void addInterest(int accountNumber)
   {
	   if (this.find(accountNumber) instanceof SavingsAccount)
	   {
		   ((SavingsAccount) this.find(accountNumber)).addInterest();
	   }
   }
   
   /**
   Finds the account and calls deductFees if it is a checking account
   @param accountNumber the number of the account to find
   */
   public void deductFees(int accountNumber)
   {
	   if (this.find(accountNumber) instanceof CheckingAccount)
	   {
		   ((CheckingAccount) this.find(accountNumber)).deductFees();
	   }
   }
   
   /**
   Transfers an amount between accounts
   @param withdrawAcctNum the number of the withdraw account
   @param depositAcctNum the number of the deposit account
   @param amount the amount to transfer
   */
   public void transfer(int withdrawAcctNum, int depositAcctNum, double amount)
   {
	   if (this.find(withdrawAcctNum).isOpen() && this.find(depositAcctNum).isOpen() && amount > 0 && amount <= this.find(withdrawAcctNum).getBalance())
	   {
		   if (this.find(withdrawAcctNum) instanceof CheckingAccount)
		   {
			   ((CheckingAccount) this.find(withdrawAcctNum)).withdraw(amount);
		   }
		   else
		   {
			   this.find(withdrawAcctNum).withdraw(amount);
		   }
		   this.find(depositAcctNum).deposit(amount);
	    }
   }
   
   /**
   Returns true if both accounts have the same balance
   @return boolean
   */
   public boolean areEqualAccounts(int accountNumber1, int accountNumber2)
   {
	   return this.find(accountNumber1).equals(this.find(accountNumber2));
   }
}